﻿@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Localization
@using AliasVault.Client.Resources
@inherits AliasVault.Client.Main.Pages.MainBase
@inject IJSRuntime JSRuntime
@inject CredentialService CredentialService
@inject AliasVault.Client.Services.QuickCreateStateService QuickCreateStateService
@inject LanguageService LanguageService
@implements IAsyncDisposable

<button @ref="buttonRef" @onclick="TogglePopup" id="quickIdentityButton" class="px-4 py-2 text-sm font-medium text-white bg-gradient-to-r from-primary-500 to-primary-600 hover:from-primary-600 hover:to-primary-700 focus:outline-none dark:from-primary-400 dark:to-primary-500 dark:hover:from-primary-500 dark:hover:to-primary-600 rounded-md shadow-sm transition duration-150 ease-in-out transform hover:scale-105 active:scale-95 focus:shadow-outline">
    @Localizer["NewAliasButtonShort"] <span class="hidden md:inline">@Localizer["NewAliasButtonText"].Value.Substring(1).Trim()</span>
</button>

@if (IsPopupVisible)
{
    <ClickOutsideHandler OnClose="ClosePopup" ContentId="quickIdentityPopup,quickIdentityButton">
        <div id="quickIdentityPopup" class="absolute z-50 mt-2 p-4 bg-white rounded-lg shadow-xl border border-gray-300 dark:bg-gray-800 dark:border-gray-400"
             style="@PopupStyle">
            <h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">@Localizer["CreateNewAliasTitle"]</h3>
            <EditForm Model="Model" OnValidSubmit="CreateIdentity">
                <DataAnnotationsValidator />
                <div class="mb-4">
                    <EditFormRow Id="serviceName" Label="@Localizer["ServiceNameLabel"]" Placeholder="@Localizer["ServiceNamePlaceholder"]" @bind-Value="Model.ServiceName"></EditFormRow>
                    <ValidationMessage For="() => Model.ServiceName"/>
                </div>
                <div class="mb-4">
                    <EditFormRow Id="serviceUrl" Label="@Localizer["ServiceUrlLabel"]" OnFocus="OnFocusUrlInput" @bind-Value="Model.ServiceUrl"></EditFormRow>
                    <ValidationMessage For="() => Model.ServiceUrl"/>
                </div>
                <div class="flex justify-between items-center">
                    <button id="quickIdentitySubmit" type="submit" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
                        @Localizer["CreateButton"]
                    </button>
                </div>
                <div class="pt-2">
                    <a href="#" @onclick="OpenAdvancedMode" @onclick:preventDefault class="text-sm text-blue-500 hover:text-blue-700">
                        @Localizer["AdvancedModeLink"]
                    </a>
                </div>
            </EditForm>
        </div>
    </ClickOutsideHandler>
}

@code {
    private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Widgets.CreateNewIdentityWidget", "AliasVault.Client");

    private bool IsPopupVisible = false;
    private bool IsCreating = false;
    private CreateModel Model = new();
    private string PopupStyle { get; set; } = string.Empty;
    private ElementReference buttonRef;
    private IJSObjectReference? Module;

    /// <inheritdoc />
    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        await KeyboardShortcutService.UnregisterShortcutAsync("gc");
        LanguageService.LanguageChanged -= OnLanguageChanged;
        if (Module is not null)
        {
            await Module.DisposeAsync();
        }
    }

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await KeyboardShortcutService.RegisterShortcutAsync("gc", ShowPopup);
            LanguageService.LanguageChanged += OnLanguageChanged;
            Module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/modules/newIdentityWidget.js");
        }
    }

    /// <summary>
    /// Handles language change events and triggers component refresh.
    /// </summary>
    /// <param name="languageCode">The new language code.</param>
    private void OnLanguageChanged(string languageCode)
    {
        InvokeAsync(StateHasChanged);
    }

    /// <summary>
    /// When the URL input is focused, place cursor at the end of the default URL to allow for easy typing.
    /// </summary>
    private void OnFocusUrlInput(FocusEventArgs e)
    {
        if (Model.ServiceUrl != CredentialService.DefaultServiceUrl)
        {
            return;
        }

        // Use a small delay to ensure the focus is set after the browser's default behavior.
        Task.Delay(1).ContinueWith(_ =>
        {
            JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('serviceUrl').setSelectionRange({CredentialService.DefaultServiceUrl.Length}, {CredentialService.DefaultServiceUrl.Length})");
        });
    }

    /// <summary>
    /// Toggle the popup.
    /// </summary>
    private async Task TogglePopup()
    {
        IsPopupVisible = !IsPopupVisible;
        if (IsPopupVisible)
        {
            await ShowPopup();
        }
    }

    /// <summary>
    /// Show the popup.
    /// </summary>
    private async Task ShowPopup()
    {
        IsPopupVisible = true;

        // Clear the input fields
        Model = new();
        Model.ServiceUrl = CredentialService.DefaultServiceUrl;

        await UpdatePopupStyle();
        await Task.Delay(100); // Give time for the DOM to update
        await JsInteropService.FocusElementById("serviceName");
    }

    /// <summary>
    /// Close the popup.
    /// </summary>
    private void ClosePopup()
    {
        IsPopupVisible = false;
    }

    /// <summary>
    /// Update the popup style so that it is positioned correctly.
    /// </summary>
    private async Task UpdatePopupStyle()
    {
        var windowWidth = await JSRuntime.InvokeAsync<int>("getWindowWidth");
        var buttonRect = await JSRuntime.InvokeAsync<BoundingClientRect>("getElementRect", buttonRef);

        // Constrain the popup width to 400px minus some padding.
        var popupWidth = Math.Min(400, windowWidth - 20);

        PopupStyle = $"width: {popupWidth}px; top: {buttonRect.Bottom}px;";
        StateHasChanged();
    }

    /// <summary>
    /// Create the new identity.
    /// </summary>
    private async Task CreateIdentity()
    {
        if (IsCreating)
        {
            return;
        }

        IsCreating = true;
        GlobalLoadingSpinner.Show(Localizer["CreatingNewAliasMessage"]);
        StateHasChanged();

        var credential = new Credential();
        credential.Alias = new Alias();
        credential.Alias.Email = "@" + CredentialService.GetDefaultEmailDomain();
        credential.Service = new Service();
        credential.Service.Name = Model.ServiceName;

        if (Model.ServiceUrl != CredentialService.DefaultServiceUrl)
        {
            credential.Service.Url = Model.ServiceUrl;
        }

        credential.Passwords = new List<Password> { new() };
        await CredentialService.GenerateRandomIdentityAsync(credential);

        var id = await CredentialService.InsertEntryAsync(credential);
        if (id == Guid.Empty)
        {
            // Error saving.
            IsCreating = false;
            GlobalLoadingSpinner.Hide();
            GlobalNotificationService.AddErrorMessage(Localizer["CreateCredentialErrorMessage"], true);
            return;
        }

        // No error, add success message.
        GlobalNotificationService.AddSuccessMessage(Localizer["CredentialCreatedSuccessMessage"]);

        NavigationManager.NavigateTo("/credentials/" + id);

        IsCreating = false;
        GlobalLoadingSpinner.Hide();
        StateHasChanged();
        ClosePopup();
    }

    /// <summary>
    /// Open the advanced mode for creating a new identity.
    /// </summary>
    private void OpenAdvancedMode()
    {
        // Store the form data in the state service to prefill in the advanced mode form.
        QuickCreateStateService.ServiceName = Model.ServiceName;
        QuickCreateStateService.ServiceUrl = Model.ServiceUrl;

        NavigationManager.NavigateTo("/credentials/create");
        ClosePopup();
    }

    /// <summary>
    /// Bounding client rectangle returned from JavaScript.
    /// </summary>
    /// <remarks>
    /// Properties are populated via JavaScript interop deserialization.
    /// SonarCloud warnings are suppressed as these are required for JS interop.
    /// </remarks>
    private sealed class BoundingClientRect
    {
        public double Left { get; set; }
        public double Top { get; set; }
        public double Right { get; set; }
        public double Bottom { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
    }

    /// <summary>
    /// Local model for the form with support for validation.
    /// </summary>
    private sealed class CreateModel
    {
        /// <summary>
        /// The service name.
        /// </summary>
        [Required(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.ServiceNameRequired))]
        [Display(Name = "Service Name")]
        public string ServiceName { get; set; } = string.Empty;

        /// <summary>
        /// The service URL.
        /// </summary>
        [Display(Name = "Service URL")]
        public string ServiceUrl { get; set; } = string.Empty;
    }
}
